package rexsee.network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;

import rexsee.core.browser.Browser;
import rexsee.core.browser.clazz.ActivityResult.ActivityResultListener;
import rexsee.core.browser.clazz.JavascriptInterface;
import rexsee.core.utilities.Escape;
import rexsee.core.utilities.RexseeClazz;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

public class RexseeBluetooth implements JavascriptInterface {

	public static final String INTERFACE_NAME = "Bluetooth";
	@Override
	public String getInterfaceName() {
		return mBrowser.application.resources.prefix + INTERFACE_NAME;
	}
	@Override
	public JavascriptInterface getInheritInterface(Browser childBrowser) {
		return this;
	}
	@Override
	public JavascriptInterface getNewInterface(Browser childBrowser) {
		return new RexseeBluetooth(childBrowser);
	}

	//Private functions
	private String getJsonInfoByDevice_(BluetoothDevice device) {
		if (device == null) {
			error(ERROR_DEVICE_ERROR);
			return "{}";
		}
		String rtn = "";
		rtn += "{";
		rtn += "\"address\":\"" + device.getAddress() + "\"";
		rtn += ",\"name\":\"" + device.getName() + "\"";
		rtn += ",\"state\":\"" + getBondState_(device.getBondState()) + "\"";
		rtn += ",\"class\":\"" + getDeviceClass_(device) + "\"";
		rtn += ",\"major\":\"" + getMajorDeviceClass_(device) + "\"";
		rtn += ",\"services\":" + getServiceClasses_(device) + "";
		rtn += "}";
		return rtn;
	}
	private String getDeviceClass_(BluetoothDevice device) {
		int c = device.getBluetoothClass().getDeviceClass();
		Field[] fields = BluetoothClass.Device.class.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			try {
				if (fields[i].getInt(null) == c) return fields[i].getName();
			} catch (Exception e) {
			}
		}
		return null;
	}
	private String getMajorDeviceClass_(BluetoothDevice device) {
		int c = device.getBluetoothClass().getMajorDeviceClass();
		Field[] fields = BluetoothClass.Device.Major.class.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			try {
				if (fields[i].getInt(null) == c) return fields[i].getName();
			} catch (Exception e) {
			}
		}
		return null;
	}
	private String getServiceClasses_(BluetoothDevice device) {
		BluetoothClass c = device.getBluetoothClass();
		Field[] fields = BluetoothClass.Service.class.getDeclaredFields();
		ArrayList<String> l = new ArrayList<String>();
		for (int i = 0; i < fields.length; i++) {
			try {
				if (c.hasService(fields[i].getInt(null))) l.add(fields[i].getName());
			} catch (Exception e) {
			}
		}
		String rtn = "[";
		for (int i = 0; i < l.size(); i++) {
			if (i != 0) rtn += ",";
			rtn += "\"" + l.get(i) + "\"";
		}
		rtn += "]";
		return rtn;
	}
	private String getBondState_(int iState) {
		switch (iState) {
			case BluetoothDevice.BOND_BONDED :
				return "bonded";
			case BluetoothDevice.BOND_BONDING :
				return "bonding";
			default :
				return "none";
		}
	}
	private String getState_(int iState) {
		switch (iState) {
			case BluetoothAdapter.STATE_ON :
				return "on";
			case BluetoothAdapter.STATE_OFF :
				return "off";
			case BluetoothAdapter.STATE_TURNING_ON :
				return "turning_on";
			case BluetoothAdapter.STATE_TURNING_OFF :
				return "turning_off";
			default :
				return "unknown";
		}
	}
	private String getConnectState_(int iState) {
		switch (iState) {
			case CONNECT_STATE_CONNECTED :
				return "connected";
			case CONNECT_STATE_CONNECTING :
				return "connecting";
			default :
				return "none";
		}

	}
	private String getScanMode_(int iMode) {
		switch (iMode) {
			case BluetoothAdapter.SCAN_MODE_CONNECTABLE :
				return "connectable";
			case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE :
				return "connectable_discoverable";
			default :
				return "none";
		}
	}
	private byte[] convertPinToBytes(String pin) {
		if (pin == null) { return null; }
		byte[] pinBytes;
		try {
			pinBytes = pin.getBytes("UTF8");
		} catch (Exception uee) {
			return null;
		}
		if (pinBytes.length <= 0 || pinBytes.length > 16) { return null; }
		return pinBytes;
	}

	private synchronized void setConnectState_(int state) {
		mPreviousConnectState = mConnectState;
		mConnectState = state;
		mBrowser.eventList.run(EVENT_CONNECT_STATE_CHANGED);
	}
	private synchronized void writeBytes_(final byte[] bytes) {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return;
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return;
		}
		if (mCurrentRemoteDevice == null) {
			error("No connected device.");
			return;
		}
		if (mConnectState != CONNECT_STATE_CONNECTED) return;
		new Thread() {
			@Override
			public void run() {
				try {
					OutputStream outStream = mSocket.getOutputStream();
					outStream.write(bytes);
					String data = Escape.escape(new String(bytes));
					mBrowser.eventList.run(EVENT_DATA_WRITE, new String[]{data});
				} catch (IOException e) {
				}
			}
		}.start();
	}

	private static final UUID UUID_SERIAL_PORT = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
	private static final String INCOMING_CONNECTION = "BluetoothIncomingConnection";

	// Constants that indicate the current connection state
	public static final int CONNECT_STATE_NONE = 0; // we're doing nothing
	public static final int CONNECT_STATE_CONNECTING = 1; // now initiating an outgoing connection
	public static final int CONNECT_STATE_CONNECTED = 2; // now connected to a remote device

	//Error messages
	public static final String ERROR_UNSUPPORTED = "Bluetooth isn't supported on this device.";
	public static final String ERROR_DISABLED = "Bluetooth is disabled.";
	public static final String ERROR_INVALIDE_ADDRESS = "Bluetooth address is invalid:";
	public static final String ERROR_DEVICE_ERROR = "Remote device doesn't exist.";
	private void error(String msg) {
		mBrowser.exception(getInterfaceName(), "Bluetooth Error: " + msg);
	}

	//startActivity Events
	public static final String EVENT_ONBLUETTOOTHENABLESUCCESSED = "onBluetoothEnableSuccessed";
	public static final String EVENT_ONBLUETTOOTHENABLEFAILED = "onBluetoothEnableFailed";
	public static final String EVENT_ONBLUETTOOTHSETDISCOVERABLESUCCESSED = "onBluetoothSetDiscoverableSuccessed";
	public static final String EVENT_ONBLUETTOOTHSETDISCOVERABLEFAILED = "onBluetoothSetDiscoverableFailed";

	//Rexsee events
	public static final String EVENT_CONNECT_STATE_CHANGED = "onBluetoothDeviceConnectStateChanged";
	public static final String EVENT_CONNECT_FAILED = "onBluetoothDeviceConnectFailed";
	public static final String EVENT_CONNECTION_REQUEST = "onBluetoothReceivedConnectRequest";
	public static final String EVENT_CONNECTION_LOST = "onBluetoothConnectionLost";
	public static final String EVENT_DATA_READ = "onBluetoothDataRead";
	public static final String EVENT_DATA_WRITE = "onBluetoothDataWrite";

	//Broadcast Events from BluetoothAdapter
	public static final String EVENT_ACTION_DISCOVERY_STARTED = "onBluetoothDiscoveringStarted";
	public static final String EVENT_ACTION_DISCOVERY_FINISHED = "onBluetoothDiscoveringFinished";
	public static final String EVENT_ACTION_LOCAL_NAME_CHANGED = "onBluetoothLocalNameChanged";
	public static final String EVENT_ACTION_SCAN_MODE_CHANGED = "onBluetoothScanModeChanged";
	public static final String EVENT_ACTION_STATE_CHANGED = "onBluetoothStateChanged";

	private final BroadcastReceiver EVENT_ACTION_DISCOVERY_STARTED_Receiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			mDeviceList.clear();
			mBrowser.eventList.run(EVENT_ACTION_DISCOVERY_STARTED);
		}
	};
	private final BroadcastReceiver EVENT_ACTION_DISCOVERY_FINISHED_Receiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			mBrowser.eventList.run(EVENT_ACTION_DISCOVERY_FINISHED);
		}
	};
	private final BroadcastReceiver EVENT_ACTION_LOCAL_NAME_CHANGED_Receiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			mBrowser.eventList.run(EVENT_ACTION_LOCAL_NAME_CHANGED);
		}
	};
	private final BroadcastReceiver EVENT_ACTION_SCAN_MODE_CHANGED_Receiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			int currentMode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
			int previousMode = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
			mBrowser.eventList.run(EVENT_ACTION_SCAN_MODE_CHANGED, new String[]{getScanMode_(currentMode), getScanMode_(previousMode)});
		}
	};
	private final BroadcastReceiver EVENT_ACTION_STATE_CHANGED_Receiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			int currentState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
			int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, BluetoothAdapter.STATE_OFF);
			mBrowser.eventList.run(EVENT_ACTION_STATE_CHANGED, new String[]{getState_(currentState), getState_(previousState)});
		}
	};

	//Broadcast events from BluetoothDevice	
	public static final String EVENT_ACTION_FOUND = "onBluetoothDeviceFound";
	public static final String EVENT_ACTION_ACL_CONNECTED = "onBluetoothDeviceACLConnected";
	public static final String EVENT_ACTION_ACL_DISCONNECTED = "onBluetoothDeviceACLDisconnected";
	public static final String EVENT_ACTION_ACL_DISCONNECT_REQUESTED = "onBluetoothDeviceACLDisconnectRequested";
	public static final String EVENT_ACTION_BOND_STATE_CHANGED = "onBluetoothDeviceBondStateChanged";
	public static final String EVENT_ACTION_CLASS_CHANGED = "onBluetoothDeviceClassChanged";
	public static final String EVENT_ACTION_NAME_CHANGED = "onBluetoothDeviceNameChanged";

	private final BroadcastReceiver EVENT_ACTION_FOUND_Receiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
			if (!mDeviceList.contains(device)) mDeviceList.add(device);
			mBrowser.eventList.run(EVENT_ACTION_FOUND, new String[]{device.getAddress()});
		}
	};
	private final BroadcastReceiver EVENT_ACTION_ACL_CONNECTED_Receiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
			mBrowser.eventList.run(EVENT_ACTION_ACL_CONNECTED, new String[]{device.getAddress()});
		}
	};
	private final BroadcastReceiver EVENT_ACTION_ACL_DISCONNECTED_Receiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
			mBrowser.eventList.run(EVENT_ACTION_ACL_DISCONNECTED, new String[]{device.getAddress()});
		}
	};
	private final BroadcastReceiver EVENT_ACTION_ACL_DISCONNECT_REQUESTED_Receiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
			mBrowser.eventList.run(EVENT_ACTION_ACL_DISCONNECT_REQUESTED, new String[]{device.getAddress()});
		}
	};
	private final BroadcastReceiver EVENT_ACTION_BOND_STATE_CHANGED_Receiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
			int currentState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
			int previousState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.BOND_NONE);
			mBrowser.eventList.run(EVENT_ACTION_BOND_STATE_CHANGED, new String[]{device.getAddress(), getBondState_(currentState), getBondState_(previousState)});
		}
	};
	private final BroadcastReceiver EVENT_ACTION_CLASS_CHANGED_Receiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
			mBrowser.eventList.run(EVENT_ACTION_CLASS_CHANGED, new String[]{device.getAddress()});
		}
	};
	private final BroadcastReceiver EVENT_ACTION_NAME_CHANGED_Receiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
			mBrowser.eventList.run(EVENT_ACTION_NAME_CHANGED, new String[]{device.getAddress()});
		}
	};

	private final Context mContext;
	private final Browser mBrowser;
	private final BluetoothAdapter mBluetoothAdapter;
	private final ArrayList<BluetoothDevice> mDeviceList = new ArrayList<BluetoothDevice>();

	private BluetoothDevice mCurrentRemoteDevice;
	private int mConnectState;
	private int mPreviousConnectState;

	private ListenerForConnection mListenerForConnection;
	private BluetoothServerSocket mServerSocket;
	private BluetoothSocket mSocket;

	private boolean mIsListeningToBroadcast = false;

	public RexseeBluetooth(Browser browser) {

		mContext = browser.getContext();
		mBrowser = browser;
		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
		mConnectState = CONNECT_STATE_NONE;
		mPreviousConnectState = CONNECT_STATE_NONE;
		mCurrentRemoteDevice = null;

		mListenerForConnection = null;
		mServerSocket = null;
		mSocket = null;

		browser.eventList.add(EVENT_ONBLUETTOOTHENABLESUCCESSED);
		browser.eventList.add(EVENT_ONBLUETTOOTHENABLEFAILED);
		browser.eventList.add(EVENT_ONBLUETTOOTHSETDISCOVERABLESUCCESSED);
		browser.eventList.add(EVENT_ONBLUETTOOTHSETDISCOVERABLEFAILED);

		browser.eventList.add(EVENT_ACTION_DISCOVERY_STARTED);
		browser.eventList.add(EVENT_ACTION_DISCOVERY_FINISHED);
		browser.eventList.add(EVENT_ACTION_LOCAL_NAME_CHANGED);
		browser.eventList.add(EVENT_ACTION_SCAN_MODE_CHANGED);
		browser.eventList.add(EVENT_ACTION_STATE_CHANGED);

		browser.eventList.add(EVENT_ACTION_FOUND);
		browser.eventList.add(EVENT_ACTION_ACL_CONNECTED);
		browser.eventList.add(EVENT_ACTION_ACL_DISCONNECTED);
		browser.eventList.add(EVENT_ACTION_ACL_DISCONNECT_REQUESTED);
		browser.eventList.add(EVENT_ACTION_BOND_STATE_CHANGED);
		browser.eventList.add(EVENT_ACTION_CLASS_CHANGED);
		browser.eventList.add(EVENT_ACTION_NAME_CHANGED);

		browser.eventList.add(EVENT_CONNECT_STATE_CHANGED);
		browser.eventList.add(EVENT_CONNECT_FAILED);
		browser.eventList.add(EVENT_CONNECTION_REQUEST);
		browser.eventList.add(EVENT_CONNECTION_LOST);
		browser.eventList.add(EVENT_DATA_READ);
		browser.eventList.add(EVENT_DATA_WRITE);

	}

	//JavaScript Interface

	public String getSupportedDeviceClasses() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return "[]";
		}
		Field[] fields = BluetoothClass.Device.class.getDeclaredFields();
		String rtn = "[";
		for (int i = 0; i < fields.length; i++) {
			if (i != 0) rtn += ",";
			rtn += "\"" + fields[i].getName() + "\"";
		}
		rtn += "]";
		return rtn;
	}
	public String getSupportedDeviceMajorClasses() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return "[]";
		}
		Field[] fields = BluetoothClass.Device.Major.class.getDeclaredFields();
		String rtn = "[";
		for (int i = 0; i < fields.length; i++) {
			if (i != 0) rtn += ",";
			rtn += "\"" + fields[i].getName() + "\"";
		}
		rtn += "]";
		return rtn;
	}
	public String getSupportServiceClasses() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return "[]";
		}
		Field[] fields = BluetoothClass.Service.class.getDeclaredFields();
		String rtn = "[";
		for (int i = 0; i < fields.length; i++) {
			if (i != 0) rtn += ",";
			rtn += "\"" + fields[i].getName() + "\"";
		}
		rtn += "]";
		return rtn;
	}

	public boolean isSupported() {
		return (mBluetoothAdapter == null) ? false : true;
	}
	public boolean isEnabled() {
		return (mBluetoothAdapter == null) ? false : mBluetoothAdapter.isEnabled();
	}
	public String getAddress() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return "";
		}
		return mBluetoothAdapter.getAddress();
	}
	public String getName() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return "";
		}
		return mBluetoothAdapter.getName();
	}
	public boolean setName(String name) {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return false;
		}
		return mBluetoothAdapter.setName(name);
	}
	public String getState() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return "";
		}
		return getState_(mBluetoothAdapter.getState());
	}
	public String getScanMode() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return "";
		}
		return getScanMode_(mBluetoothAdapter.getScanMode());
	}
	public boolean isDiscoverable() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return false;
		}
		return (mBluetoothAdapter.getScanMode() == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) ? true : false;
	}
	public boolean isDiscovering() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return false;
		}
		return mBluetoothAdapter.isDiscovering();
	}

	public boolean checkAddress(String address) {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return false;
		}
		return BluetoothAdapter.checkBluetoothAddress(address);
	}

	public String getDeviceInformation(String address) {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return "{}";
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return "{}";
		}
		if (!BluetoothAdapter.checkBluetoothAddress(address)) {
			error(ERROR_INVALIDE_ADDRESS + address);
			return "{}";
		}
		return getJsonInfoByDevice_(mBluetoothAdapter.getRemoteDevice(address));
	}
	public String getCurrentDevices() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return "[]";
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return "[]";
		}
		if (mDeviceList.size() == 0) return "[]";
		String rtn = "[";
		for (int i = 0; i < mDeviceList.size(); i++) {
			if (i != 0) rtn += ",";
			rtn += getJsonInfoByDevice_(mDeviceList.get(i));
		}
		rtn += "]";
		return rtn;
	}
	public String getBondedDevices() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return "[]";
		}
		Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
		Iterator<BluetoothDevice> iterator = devices.iterator();
		String rtn = "";
		while (iterator.hasNext()) {
			if (!rtn.equals("")) rtn += ",";
			rtn += getJsonInfoByDevice_(iterator.next());
		}
		rtn = "[" + rtn + "]";
		return rtn;
	}

	public void enable() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return;
		}
		Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
		mBrowser.activityResult.start(intent, new ActivityResultListener() {
			@Override
			public void run(int resultCode, Intent resultIntent) {
				if (isEnabled()) {
					mBrowser.eventList.run(EVENT_ONBLUETTOOTHENABLESUCCESSED);
				} else {
					mBrowser.eventList.run(EVENT_ONBLUETTOOTHENABLEFAILED);
				}
			}
		});
	}
	public boolean forceEnable() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return false;
		}
		return mBluetoothAdapter.enable();
	}
	public boolean disable() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return false;
		}
		return mBluetoothAdapter.disable();
	}
	public void setDiscoverable(int seconds) {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return;
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return;
		}
		if (seconds > 300) {
			error("Too long dicoverable duration, it will be cut to 300 seconds.");
		}
		Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
		intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, seconds);
		mBrowser.activityResult.start(intent, new ActivityResultListener() {
			@Override
			public void run(int resultCode, Intent resultIntent) {
				if (isDiscoverable()) {
					mBrowser.eventList.run(EVENT_ONBLUETTOOTHSETDISCOVERABLESUCCESSED, new String[]{String.valueOf(resultCode)});
				} else {
					mBrowser.eventList.run(EVENT_ONBLUETTOOTHSETDISCOVERABLEFAILED);
				}
			}
		});
	}

	public boolean startDiscovery() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return false;
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return false;
		}
		return mBluetoothAdapter.startDiscovery();
	}
	public boolean cancelDiscovery() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return false;
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return false;
		}
		return mBluetoothAdapter.cancelDiscovery();
	}

	public boolean createBond(String address) {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return false;
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return false;
		}
		if (!BluetoothAdapter.checkBluetoothAddress(address)) {
			error(ERROR_INVALIDE_ADDRESS + address);
			return false;
		}
		BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
		if (device == null) {
			error(ERROR_DEVICE_ERROR);
			return false;
		}
		try {
			Method createBondMethod = BluetoothDevice.class.getMethod("createBond");
			return (Boolean) createBondMethod.invoke(device);
		} catch (Exception e) {
			error(e.getLocalizedMessage());
			return false;
		}
	}
	public boolean createBond(String address, String pin) {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return false;
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return false;
		}
		if (!BluetoothAdapter.checkBluetoothAddress(address)) {
			error(ERROR_INVALIDE_ADDRESS + address);
			return false;
		}
		BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
		if (device == null) {
			error(ERROR_DEVICE_ERROR);
			return false;
		}
		byte[] pinBytes = convertPinToBytes(pin);
		if (pinBytes == null) {
			error("Invalid pin code.");
			return false;
		}
		try {
			Method setPinMethod = RexseeClazz.getMethod(BluetoothDevice.class, "setPin");
			if (setPinMethod == null) {
				error("setPin() method is not found.");
				return false;
			}
			Method createBondMethod = BluetoothDevice.class.getMethod("createBond");
			if (createBondMethod == null) {
				error("createBond() method is not found.");
				return false;
			}
			//setPin(byte[]) doesn't work
			//if (!((Boolean) setPinMethod.invoke(device, new Object[]{device.getAddress(), pinBytes}))) {
			if (!((Boolean) setPinMethod.invoke(device, new Object[]{pinBytes}))) {
				error("Set pin code failed.");
			}
			return (Boolean) createBondMethod.invoke(device);
		} catch (Exception e) {
			error(e.getLocalizedMessage());
			return false;
		}
	}
	public boolean removeBond(String address) {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return false;
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return false;
		}
		if (!BluetoothAdapter.checkBluetoothAddress(address)) {
			error(ERROR_INVALIDE_ADDRESS + address);
			return false;
		}
		BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
		if (device == null) {
			error(ERROR_DEVICE_ERROR);
			return false;
		}
		try {
			Method removeBondMethod = BluetoothDevice.class.getMethod("removeBond");
			return (Boolean) removeBondMethod.invoke(device);
		} catch (Exception e) {
			error(e.getLocalizedMessage());
			return false;
		}
	}

	public boolean isListeningToBroascast() {
		return mIsListeningToBroadcast;
	}
	public boolean startListenerForBroadcast() {

		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return false;
		}

		if (mIsListeningToBroadcast) return true;

		mContext.registerReceiver(EVENT_ACTION_DISCOVERY_STARTED_Receiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED));
		mContext.registerReceiver(EVENT_ACTION_DISCOVERY_FINISHED_Receiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED));
		mContext.registerReceiver(EVENT_ACTION_LOCAL_NAME_CHANGED_Receiver, new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED));
		mContext.registerReceiver(EVENT_ACTION_SCAN_MODE_CHANGED_Receiver, new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED));
		mContext.registerReceiver(EVENT_ACTION_STATE_CHANGED_Receiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));

		mContext.registerReceiver(EVENT_ACTION_FOUND_Receiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
		mContext.registerReceiver(EVENT_ACTION_ACL_CONNECTED_Receiver, new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED));
		mContext.registerReceiver(EVENT_ACTION_ACL_DISCONNECTED_Receiver, new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED));
		mContext.registerReceiver(EVENT_ACTION_ACL_DISCONNECT_REQUESTED_Receiver, new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED));
		mContext.registerReceiver(EVENT_ACTION_BOND_STATE_CHANGED_Receiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
		mContext.registerReceiver(EVENT_ACTION_CLASS_CHANGED_Receiver, new IntentFilter(BluetoothDevice.ACTION_CLASS_CHANGED));
		mContext.registerReceiver(EVENT_ACTION_NAME_CHANGED_Receiver, new IntentFilter(BluetoothDevice.ACTION_NAME_CHANGED));

		mIsListeningToBroadcast = true;

		return true;

	}
	public boolean stopListenerForBroadcast() {

		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return false;
		}

		if (!mIsListeningToBroadcast) return true;

		mContext.unregisterReceiver(EVENT_ACTION_DISCOVERY_STARTED_Receiver);
		mContext.unregisterReceiver(EVENT_ACTION_DISCOVERY_FINISHED_Receiver);
		mContext.unregisterReceiver(EVENT_ACTION_LOCAL_NAME_CHANGED_Receiver);
		mContext.unregisterReceiver(EVENT_ACTION_SCAN_MODE_CHANGED_Receiver);
		mContext.unregisterReceiver(EVENT_ACTION_STATE_CHANGED_Receiver);

		mContext.unregisterReceiver(EVENT_ACTION_FOUND_Receiver);
		mContext.unregisterReceiver(EVENT_ACTION_ACL_CONNECTED_Receiver);
		mContext.unregisterReceiver(EVENT_ACTION_ACL_DISCONNECTED_Receiver);
		mContext.unregisterReceiver(EVENT_ACTION_ACL_DISCONNECT_REQUESTED_Receiver);
		mContext.unregisterReceiver(EVENT_ACTION_BOND_STATE_CHANGED_Receiver);
		mContext.unregisterReceiver(EVENT_ACTION_CLASS_CHANGED_Receiver);
		mContext.unregisterReceiver(EVENT_ACTION_NAME_CHANGED_Receiver);

		mIsListeningToBroadcast = false;

		return true;

	}

	public boolean isListeningToConnectRequest() {
		return (mListenerForConnection == null) ? false : true;
	}
	public boolean startListenerForConnectRequest() {
		disconnect();
		stopListenerForConnectRequest();
		try {
			mServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(INCOMING_CONNECTION, UUID_SERIAL_PORT);
			mListenerForConnection = new ListenerForConnection();
			mListenerForConnection.start();
			return true;
		} catch (Exception e) {
			error(e.getLocalizedMessage());
			return false;
		}
	}
	public boolean stopListenerForConnectRequest() {
		try {
			if (mListenerForConnection != null) {
				mListenerForConnection = null;
			}
			if (mServerSocket != null) {
				mServerSocket.close();
				mServerSocket = null;
			}
			return true;
		} catch (Exception e) {
			error(e.getLocalizedMessage());
			return false;
		}
	}
	public void accept() {
		new BluetoothConnection().start();
	}
	public void deny() {
		startListenerForConnectRequest();
	}

	public synchronized void connect(String address) {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return;
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return;
		}
		if (!BluetoothAdapter.checkBluetoothAddress(address)) {
			error(ERROR_INVALIDE_ADDRESS + address);
			return;
		}
		BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
		if (device == null) {
			error(ERROR_DEVICE_ERROR);
			return;
		}
		disconnect();
		stopListenerForConnectRequest();
		setConnectState_(CONNECT_STATE_CONNECTING);
		mCurrentRemoteDevice = device;
		new Thread() {
			@Override
			public void run() {
				mBluetoothAdapter.cancelDiscovery();
				try {
					mSocket = mCurrentRemoteDevice.createRfcommSocketToServiceRecord(UUID_SERIAL_PORT);
					mSocket.connect();
					new BluetoothConnection().start();
				} catch (IOException e) {
					mBrowser.eventList.run(EVENT_CONNECT_FAILED);
					disconnect();
				}
			}
		}.start();
	}
	public synchronized void disconnect() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return;
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return;
		}
		if (mSocket != null) {
			try {
				mSocket.close();
				mSocket = null;
			} catch (IOException e) {
			}
		}
		setConnectState_(CONNECT_STATE_NONE);
		mCurrentRemoteDevice = null;
	}

	public boolean isConnected() {
		return (mConnectState == CONNECT_STATE_CONNECTED) ? true : false;
	}
	public String getConnectState() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return "none";
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return "none";
		}
		return getConnectState_(mConnectState);
	}
	public String getPreviousConnectState() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return "none";
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return "none";
		}
		return getConnectState_(mPreviousConnectState);
	}
	public String getCurrentDevice() {
		if (mBluetoothAdapter == null) {
			error(ERROR_UNSUPPORTED);
			return "{}";
		}
		if (!mBluetoothAdapter.isEnabled()) {
			error(ERROR_DISABLED);
			return "{}";
		}
		if (mCurrentRemoteDevice == null) {
			error("No current remote device.");
			return "{}";
		}
		return getJsonInfoByDevice_(mCurrentRemoteDevice);
	}

	public void writeASCII(String ascii) {
		try {
			String[] as = ascii.split(",");
			byte[] bytes = new byte[as.length];
			for (int i = 0; i < as.length; i++) {
				bytes[i] = (byte) Integer.parseInt(as[i]);
			}
			writeBytes_(bytes);
		} catch (Exception e) {
			error("Invalid ASCII code.");
		}
	}
	public void write(String str, String encoding) {
		try {
			writeBytes_(str.getBytes(encoding));
		} catch (UnsupportedEncodingException e) {
		}
	}

	private class ListenerForConnection extends Thread {
		@Override
		public void run() {
			while (mConnectState != CONNECT_STATE_CONNECTED && mServerSocket != null) {
				try {
					mSocket = mServerSocket.accept();
				} catch (IOException e) {
					mBrowser.exception(getInterfaceName(), e);
					break;
				}
				if (mSocket != null) {
					mCurrentRemoteDevice = mSocket.getRemoteDevice();
					mBrowser.eventList.run(EVENT_CONNECTION_REQUEST);
				}
			}
		}
	}
	private class BluetoothConnection extends Thread {
		@Override
		public void run() {
			setConnectState_(CONNECT_STATE_CONNECTED);
			try {
				InputStream inputStream = mSocket.getInputStream();
				while (true) {
					try {
						int length = inputStream.available();
						if (length == 0) {
							Thread.sleep(100);
						} else {
							byte[] buffer = new byte[length];
							inputStream.read(buffer);
							String data = new String(buffer, mBrowser.application.getDefaultEncoding());
							data = Escape.escape(data);
							mBrowser.eventList.run(EVENT_DATA_READ, new String[]{data});
						}
					} catch (Exception e) {
						mBrowser.eventList.run(EVENT_CONNECTION_LOST);
						break;
					}
				}
			} catch (IOException e) {
			}
		}
	}

}
